package cn.tianyuhan.util;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RuntimeUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONException;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.Getter;
import lombok.Setter;

import java.io.File;
import java.util.Optional;

/**
 * @ClassName FileInfoUtil
 * @Description
 * @Author TianYuhan
 * @Date 2020-09-29 17:22
 * @Version 1.0
 **/
@Getter
@Setter
public class FileInfoUtil {

    static {
        File ffprobe = new File("/usr/bin/ffprobe");
        FileUtil.writeFromStream(
                FileHandleUtil.class.getClassLoader().getResourceAsStream("ffprobe"),
                ffprobe);
        ffprobe.setExecutable(true, false);
        ffprobe.setReadable(true, false);
        ffprobe.setWritable(true, false);
    }


    //ffprobe常用命令
    private static final String FFPROBE = "ffprobe";
    private static final String QUIET_PRINT = "quiet";
    private static final String SHOW_FORMAT = "-show_format";
    private static final String SHOW_STREAMS = "-show_streams";
    private static final String SHOW_FRAMES = "-show_frames";
    private static final String SHOW_PACKETS = "-show_packets";
    private static final String PRINT_FORMAT = "json";
    private static final String CODEC_TYPE = "codec_type";
    private static final String AUDIO_TYPE = "audio";
    private static final String BIT_RATE = "bit_rate";
    private static final String CHANNELS = "channels";
    private static final String SAMPLE_RATE = "sample_rate";
    private static final String DURATION_TIME = "duration";
    private static final String CODEC_NAME = "codec_name";
    private static final String START_TIME = "start_time";
    private static final String FRAMES = "nb_read_frames";

    //用来保存当前文件信息
    private final JSONObject allInfo;
    private final JSONObject formatInfo;
    private final JSONArray streamInfo;
    private final JSONObject frameInfo;
    private int channel;
    private int sampleRate;
    private int bitRate;
    private String codecName;
    private double startTime;
    private double durationTime;
    private long frames;
    private File file;

    public FileInfoUtil(File file) {
        this.allInfo = getAllInfo(file);
        if (allInfo.size() == 0) {
            throw new RuntimeException("文件类型不正确:非可解析的音频文件");
        }
        this.file = file;
        formatInfo = Optional.ofNullable(this.allInfo.getJSONObject("format"))
                .orElseThrow(() -> new RuntimeException("未获取到format信息"));
        streamInfo = Optional.ofNullable(this.allInfo.getJSONArray("streams"))
                .orElseThrow(() -> new RuntimeException("未获取到stream信息"));
        frameInfo = Optional.ofNullable(this.allInfo.getJSONObject("packets_and_frames"))
                .orElseThrow(() -> new RuntimeException("未获取到frame信息"));
        getBasicAudioInfo();
    }

    /***
     * @Description 获取文件的全部信息
     * @param file
     * @return
     * @throws RuntimeException
     */
    private static JSONObject getAllInfo(File file) {
        final String allInfo = RuntimeUtil.execForStr(
                FFPROBE, "-v", QUIET_PRINT, "-of",
                PRINT_FORMAT, SHOW_FORMAT, SHOW_FRAMES, SHOW_PACKETS, SHOW_STREAMS,
                file.getAbsolutePath());
        try {
            return JSONUtil.parseObj(allInfo);
        } catch (Exception e) {
            throw new RuntimeException("文件类型不正确:非可解析的音频文件");
        }
    }

    /***
     * @Description 获取基础音频文件信息(声道数 、 比特率 、 采样率)并记录
     * @throws JSONException
     */
    private void getBasicAudioInfo() {
        for (Object jsonObject : this.streamInfo) {
            if (!(jsonObject instanceof JSONObject)) {
                throw new JSONException("不正确的StreamInfo格式");
            } else {
                JSONObject streamInfo = (JSONObject) jsonObject;
                if (streamInfo.getStr(CODEC_TYPE).equals(AUDIO_TYPE)) {
                    this.bitRate = streamInfo.getInt(BIT_RATE, 0);
                    this.channel = streamInfo.getInt(CHANNELS, 0);
                    this.sampleRate = streamInfo.getInt(SAMPLE_RATE, 0);
                    this.startTime = streamInfo.getDouble(START_TIME, 0.0);
                    this.durationTime = streamInfo.getDouble(DURATION_TIME, 0.0);
                    this.frames = streamInfo.getLong(FRAMES, 0L);
                    this.codecName = streamInfo.getStr(CODEC_NAME, "NULL");
                } else {
                    continue;
                }
            }
        }
    }

    public String getFileType() {
        return this.formatInfo.getStr("format_name");
    }
}
